package org.bonej.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;

import org.doube.util.UsageReporter;

import ij.IJ;
import ij.ImagePlus;
import ij.io.FileInfo;
import ij.io.OpenDialog;
import ij.plugin.PlugIn;
import ij.process.ByteProcessor;

/**
 * Kontron IMG reader plugin for ImageJ
 * Copyright 2014 Michael Doube
 *
 *This program is free software: you can redistribute it and/or modify
 *it under the terms of the GNU General Public License as published by
 *the Free Software Foundation, either version 3 of the License, or
 *(at your option) any later version.
 *
 *This program is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *GNU General Public License for more details.
 *
 *You should have received a copy of the GNU General Public License
 *along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * This plugin implements the Import > Kontron IMG command.
 *
 * Kontron IMG files were generated by a Kontron controller computer attached to
 * Alan Boyde's Zeiss DSM 962 scanning electron microscope. They are 8-bit
 * greyscale images with a 128-byte header, which contains the image width and
 * height and a magic number.
 *
 * @author Michael Doube, RVC, London, UK.
 */
public class KontronIMGReader implements PlugIn {

	/** All IMG start with 01 00 47 12 6D B0 (hex view) */
	private static final byte[] MAGIC = { 1, 0, 71, 18, 109, -80 };

	/** 128-byte header */
	private static final int HEADER_LENGTH = 128;

	public void run(final String arg) {

		final OpenDialog od = new OpenDialog("Open IMG...", arg);
		final String directory = od.getDirectory();
		final String fileName = od.getFileName();
		final String path = directory + fileName;
		if (fileName == null)
			return;
		if (!isKontronIMG(path)) {
			IJ.error("IMG Reader", "Not an IMG file. Magic number does not match.");
			return;
		}

		// Open the file
		try {
			final ImagePlus imp = openKontronIMG(path);
			imp.show();
			UsageReporter.reportEvent(this).send();
		} catch (final IllegalArgumentException e) {
			IJ.error("IMG Reader", e.getMessage());
			return;
		}
	}

	/**
	 * Opens a Kontron IMG file as an ImageJ ImagePlus
	 *
	 * @param path
	 * @return
	 */
	public ImagePlus openKontronIMG(final String path) {

		final int[] imageSize = getImageSize(path);
		final int width = imageSize[0];
		final int height = imageSize[1];

		// FileInfo
		final FileInfo fi = new FileInfo();
		fi.fileName = new File(path).getName();
		fi.directory = new File(path).getParent() + ((IJ.isWindows()) ? "\\" : "/");
		fi.width = width;
		fi.height = height;

		fi.intelByteOrder = true;
		fi.whiteIsZero = false;
		fi.fileType = FileInfo.GRAY8;

		final ByteProcessor bp = new ByteProcessor(width, height, readBytes(path, HEADER_LENGTH, width * height));
		final ImagePlus imp = new ImagePlus(fi.fileName, bp);
		imp.setFileInfo(fi);
		return imp;
	}

	/**
	 * Check the magic number to determine if a file is a Kontron IMG
	 *
	 * @param path
	 * @return true if the file is a Kontron IMG
	 */
	public boolean isKontronIMG(final String path) {
		return Arrays.equals(readBytes(path, 0, 6), MAGIC);
	}

	/**
	 * Get the pixel size of the image
	 *
	 * @param path
	 * @return {x, y} image size in pixels
	 */
	public int[] getImageSize(final String path) {
		final int[] sizes = { readShort(path, 6), readShort(path, 8) };
		return sizes;
	}

	/**
	 * Read bytes from a file into a byte array
	 *
	 * @param path
	 *            file to read
	 * @param firstByte
	 *            first byte to read from the file
	 * @param length
	 *            the number of bytes to read
	 * @return byte[] array of length 'length'
	 */
	private byte[] readBytes(final String path, final int firstByte, final int length) {
		if (path == null)
			throw new IllegalArgumentException();
		try {
			final File iFile = new File(path);
			final FileInputStream p = new FileInputStream(iFile);
			p.skip(firstByte);
			final byte[] bytes = new byte[length];
			p.read(bytes, 0, length);
			p.close();
			return bytes;
		} catch (final IOException e) {
			IJ.handleException(e);
		}
		return null;
	}

	private int readShort(final String path, final int firstByte) {
		if (path == null)
			throw new IllegalArgumentException();
		try {
			final File iFile = new File(path);
			final FileInputStream p = new FileInputStream(iFile);
			p.skip(firstByte);
			final int value = (p.read() + p.read() * 256);
			p.close();
			return value;
		} catch (final IOException e) {
			IJ.handleException(e);
		}
		return -1;
	}
}